<?php
/**
 * Shopware 5
 * Copyright (c) shopware AG
 *
 * According to our dual licensing model, this program can be used either
 * under the terms of the GNU Affero General Public License, version 3,
 * or under a proprietary license.
 *
 * The texts of the GNU Affero General Public License with an additional
 * permission and of our proprietary license can be found at and
 * in the LICENSE file you have received along with this program.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 * GNU Affero General Public License for more details.
 *
 * "Shopware" is a registered trademark of shopware AG.
 * The licensing of the program under the AGPLv3 does not imply a
 * trademark license. Therefore any rights, title and interest in
 * our trademarks remain entirely with us.
 */

namespace Shopware\Commands;

use Shopware\Components\Install\Database;
use Shopware\Components\Migrations\Manager;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Console\Style\SymfonyStyle;

/**
 * @category  Shopware
 * @package   Shopware\Command
 * @copyright Copyright (c) shopware AG (http://www.shopware.de)
 */
class DatabaseSetupCommand extends ShopwareCommand
{
    private $validSteps = [
        'drop',
        'create',
        'clear',
        'import',
        'importDemodata',
        'setupShop',
    ];

    /**
     * {@inheritdoc}
     */
    protected function configure()
    {
        $this->setName('sw:database:setup');

        $this->addOption(
            'steps',
            null,
            InputOption::VALUE_REQUIRED,
            sprintf("Valid steps: %s.", implode(', ', $this->validSteps))
        );

        $this->addOption(
            'host',
            null,
            InputOption::VALUE_OPTIONAL
        );

        $this->addOption(
            'path',
            null,
            InputOption::VALUE_OPTIONAL
        );
    }

    /**
     * {@inheritdoc}
     */
    protected function execute(InputInterface $input, OutputInterface $output)
    {
        $io = new SymfonyStyle($input, $output);

        if (!$input->getOption('steps')) {
            $io->error('Parameter --steps not given');
            return 1;
        }

        $dbConfig = $this->getContainer()->getParameter('shopware.db');
        $rootDir = $this->getContainer()->getParameter('kernel.root_dir');

        $connection = $this->createConnection($dbConfig);
        $database = new Database($connection);

        $steps = $input->getOption('steps');
        $steps = explode(',', $steps);

        foreach ($steps as $step) {
            if (!in_array($step, $this->validSteps)) {
                $io->error(
                    sprintf("Unknown install step (%s). Valid steps: %s\n", $step, implode(', ', $this->validSteps))
                );
                return 1;
            }
        }

        if (in_array('setupShop', $steps)) {
            $host = $input->getOption('host');
            $path = $input->getOption('path');

            $path = !empty($path) ? $path : '';
            $host = !empty($host) ? $host : '';

            if ($path === '/') {
                $path = '';
            }

            if (!empty($path)) {
                $path = trim($path, '/');
                $path = '/'.$path;
            }
        }

        while ($step = array_shift($steps)) {
            switch ($step) {
                case "drop":
                    $io->comment("Drop database");
                    $database->dropDatabase($dbConfig['dbname']);
                    break;

                case "create":
                    $io->comment("Create database");
                    $database->createDatabase($dbConfig['dbname']);
                    break;

                case "clear":
                    $io->comment("Clear database");
                    $database->emptyDatabase($dbConfig['dbname']);
                    break;

                case "import":
                    $io->comment("Import database");
                    $database->importFile($dbConfig['dbname'], $rootDir . '/_sql/install/latest.sql');

                    $migrationManger = new Manager($connection, $rootDir . '/_sql/migrations');
                    $migrationManger->run();
                    break;

                case "importDemodata":
                    $io->comment("Import demodata");
                    $database->importFile($dbConfig['dbname'], $rootDir . '/_sql/demo/latest.sql');
                    break;

                case "setupShop":
                    $io->comment("Setup shop");
                    $database->setupShop($host, $path, $dbConfig['dbname']);
                    break;

                default:
                    $io->error(sprintf("Unknown install step (%s). Valid steps: %s\n", $step, implode(', ', $this->validSteps)));
                    return 1;
            }
        }

        $io->success("Database was successfully created.");
    }

    /**
     * @param array $dbConfig
     * @return string
     */
    private function buildConnectionString(array $dbConfig)
    {
        if (!isset($dbConfig['host']) || empty($dbConfig['host'])) {
            $dbConfig['host'] = 'localhost';
        }

        $connectionSettings = array(
            'host=' . $dbConfig['host'],
        );

        if (!empty($dbConfig['socket'])) {
            $connectionSettings[] = 'unix_socket=' . $dbConfig['socket'];
        }

        if (!empty($dbConfig['port'])) {
            $connectionSettings[] = 'port=' . $dbConfig['port'];
        }

        if (!empty($dbConfig['charset'])) {
            $connectionSettings[] = 'charset=' . $dbConfig['charset'];
        }

        return implode(';', $connectionSettings);
    }

    /**
     * @param array $dbConfig
     * @return \PDO
     */
    private function createConnection(array $dbConfig)
    {
        $password = isset($dbConfig['password']) ? $dbConfig['password'] : '';
        $connectionString = $this->buildConnectionString($dbConfig);

        try {
            $conn = new \PDO(
                'mysql:' . $connectionString,
                $dbConfig['username'],
                $password
            );

            $conn->setAttribute(\PDO::ATTR_ERRMODE, \PDO::ERRMODE_EXCEPTION);
            $conn->setAttribute(\PDO::ATTR_DEFAULT_FETCH_MODE, \PDO::FETCH_ASSOC);

            // Reset sql_mode "STRICT_TRANS_TABLES" that will be default in MySQL 5.6
            $conn->exec('SET @@session.sql_mode = ""');
        } catch (\PDOException $e) {
            echo 'Could not connect to database: ' . $e->getMessage();
            exit(1);
        }

        return $conn;
    }
}
